package com.woniuxy.amusementparkos.exception.lzw.unifyhanding.handing;

import com.google.common.collect.ImmutableMap;
import com.woniuxy.amusementparkos.exception.lzw.KnownException;
import com.woniuxy.amusementparkos.exception.lzw.unifyhanding.code.PublicCode;
import com.woniuxy.amusementparkos.exception.lzw.unifyhanding.code.enumwarehouse.PublicEnumCode;
import com.woniuxy.amusementparkos.util.lzw.result.ResponseResult;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;

import java.io.EOFException;
import java.io.FileNotFoundException;

/**
 * 异常捕获
 *
 *
 * @author lizhiwen
 * @create 2020-04-29 11:13 下午
 */
@ControllerAdvice
@Slf4j
public class CatchException {

    /**
     * 可知异常
     *
     * @param e  * @return
     */
    @ResponseBody
    @ExceptionHandler(KnownException.class)
    public ResponseResult catchException(KnownException e){
        log.error("catch execption:",e.getMessage(),e);// 日志录入捕获的异常
        PublicCode publicCode = e.getPublicCode();
        return new ResponseResult(publicCode); // 返回值获取操作人员应该获取的异常信息
    }


    /**
     * 不可知异常
     *
     *   执行逻辑：PublicEnumCode中的异常code和对应的异常类，在加载时，以键值对 <异常类（键），异常信息（值）>的方式放入集合，当实际异常 = map中put的异常时，
     *           将得到一个json返回值，用以说明异常内容。
     *               1）开发者，可以根据code，比对PublicEnumCode中注释说明，查获异常类型，或从日志查获详细异常信息；
     *               2）工作人员，可以通过msg，得到异常反馈。
     *               3）对于map中没有，但仍不可知的异常，将返回一个{false,400,失败}json信息，开发者可以在日志中查获详细异常记录。
     *
     *   知识补充：使用了 不可变 且 线程安全 的Map集合 —— ImmutableMap。
     *           其核心思想为Immutable对象：
     *               1）主要用在并发（多线程）操作上进行，可在无锁机制下，进行线程间共享；
     *               2）因为不使用synchroinzed，而有效的提高了程序性能；
     *           但其缺陷是，因为不可变，导致了不重用后（极端情况），产生大量垃圾。
     *
     *   其他补充：并发常用容器并非Immutable类型容器，java1.5后提供了多个并发容器，
     *           比如基于队列的Queue和BlockingQueue，以及面试常问的ConcurrentHashMap（1.7和1.8的区别）
     *           其中，常用的并发Map容器则是ConcurrentHashMap
     */

    /**
     *  定义map
     */
    static ImmutableMap<Class<? extends Throwable>,PublicCode> unKnownexceptions;

    /**
     * 创造map实例
     */
    static ImmutableMap.Builder<Class<? extends Throwable>,PublicCode> builder = ImmutableMap.builder();

    /**
     * 启动时运行，主动执行
     */
    static {
        /*
        添加常见异常
                将 PublicEnumCode 中的异常code，以及其对应对异常类
                以键值对方式推入Map集合
         */
        builder.put(NullPointerException.class,PublicEnumCode.NPE);
        builder.put(ClassNotFoundException.class, PublicEnumCode.CLASS_NOT_FIND);
        builder.put(IndexOutOfBoundsException.class,PublicEnumCode.INDEX_OUT_OF_BOUNDS);
        builder.put(IllegalArgumentException.class,PublicEnumCode.ILLEGAL_ARG);
        builder.put(IllegalAccessException.class,PublicEnumCode.ILLEGAL_ACC);
        builder.put(ArithmeticException.class,PublicEnumCode.ARITHMETIC);
        builder.put(ClassCastException.class,PublicEnumCode.CLASS_CAST);
        builder.put(ArrayStoreException.class,PublicEnumCode.ARRAY_STORE);
        builder.put(NoSuchMethodException.class,PublicEnumCode.NO_SUCH_METHOD);
        builder.put(InstantiationException.class,PublicEnumCode.INSTANTIATION);
        builder.put(InterruptedException.class,PublicEnumCode.INTERRUPTED);
        builder.put(OutOfMemoryError.class,PublicEnumCode.OUT_OF_MEMORY);
        builder.put(NoClassDefFoundError.class,PublicEnumCode.NO_CLASS_DEF_FOUNT);
        builder.put(FileNotFoundException.class,PublicEnumCode.FILE_NOT_FOUNT);
        builder.put(EOFException.class,PublicEnumCode.EOF);
        builder.put(IllegalStateException.class,PublicEnumCode.ILLEGAL_STATE);
        // TODO：添加常见异常 （2020。4。29 0:04）
    }

    /**
     * 获取不可知异常
     *
     * @param e
     * @return
     */
    @ResponseBody
    @ExceptionHandler(Exception.class)
    public ResponseResult getUnKnownException(Exception e){
        log.error("catch UnKnownException:",e.getMessage(),e);

        if(unKnownexceptions == null){
            unKnownexceptions = builder.build();
        }
        /*
        final 使用原因
            Immutable对象，要求其元素是private 和 final的
         */
        final PublicCode publicCode = unKnownexceptions.get(e.getClass());
        final ResponseResult responseResult;
        if(publicCode != null){
            responseResult = new ResponseResult(publicCode);
        }else {
            responseResult = new ResponseResult(PublicEnumCode.SORRY);
        }
        return responseResult;
    }

}
